#include <unistd.h> //符号常量，包含了许多系统服务的函数原型
#include <sys/types.h> //基本系统数据类型，包含各类系统定义的数据类型
#include <sys/wait.h> //进程控制
#include <stdlib.h> //标准程序库函数
#include <sys/stat.h> //文件状态头文件
#include <fcntl.h> //文件控制头文件

char *path; 
 while(1)
	{ 
 path=get_current_dir_name(); 
 printf("%s>$"，path);
 /* 开始获取输入 */ 
 for(count=0;count<10;count++) 
 li_inputlen[count] = 0; 
 lc_char = getchar(); 
 while(lc_char != '\n' && count < BUFFERSIZE) 
 { 
 if(lc_char != ';') 
 BUFFER[num][li_inputlen[num] ++] = lc_char; 
 else num++; 
 lc_char = getchar(); 
 count++; 
 }
 
 /*命令超长处理*/ 
 if(count > BUFFERSIZE) 
 { 
 printf("Your command too long ! Please reenter your command !\n"); 
 count = 0; /* Reset */ 
 continue; 
 } 
 else 
 { 
 for(count=0;count<=num;count++) 
 BUFFER[count][li_inputlen[count]] = '\0'; /*加上串结束符，形成字符串*/ 
 }
 
 for(count=0;count<=num;count++) 
 { 
 input[count] = (char *)malloc(sizeof(char) * (li_inputlen[count] + 1)); 
 strcpy(input[count], BUFFER[count]); 
 }
 
 /*解析命令， 获取命令和参数并保存在 arg 中*/ 
for(count=0;count<=num;count++) 
{ 
strcpy(buffer, BUFFER[count]); 
for(i=0,j=0,k=0; i<=li_inputlen[count]; i++) 
 { 
 
 /*管道和重定向单独处理*/ 
 if(input[count][i] == '<' || input[count][i] == '>' || input[count][i] == '|') 
 { 
 if(input[count][i] == '|') 
 pipel(input[count], li_inputlen[count]); 
 else 
 redirect(input[count],li_inputlen[count]); 
 is_bj = 1; 
 break; 
 }
 
 /*处理空格、TAB 和结束符。不用处理 '\n'*/ 
 if (input[count][i] == ' ' || input[count][i] =='\t' || input[count][i] == '\0') 
 { 
 if (j == 0) /*这个条件可以略去连在一起的多个空格或者 tab*/ 
 continue; 
 else 
 { 
 buffer[j++] = '\0'; 
 arg[k] = (char *) malloc(sizeof(char)*j); 
 /*将指令或参数从缓存拷贝到 arg 中*/ 
 strcpy(arg[k],buffer); 
 j = 0; /*准备取下一个参数*/ 
 k++; 
 } 
 }
 
 else 
 { 
 /*如果字串最后是'&'，则置后台运行标记为 1*/
 
 if (input[count][i] == '&' && input[count][i+1] == '\0') 
 { 
 is_back = 1; 
 continue; 
 } 
 buffer[j++] = input[count][i]; 
 } 
} 
free(input[count]);/*释放空间*/

/*如果输入的指令是 bye 或 exit，则退出 while，即退出程序*/ 
if (strcmp(arg[0],"bye") == 0 || strcmp(arg[0],"exit") == 0) 
{ 
 printf("See later. Lucky!\n"); 
 break; 
}

if (is_bj == 0) 
{ 
/*非管道、重定向指令,在使用 xxec 执行命令的时候，最后的参数必须是 NULL 指针，
所以将最后一个参数置成空值*/ 
arg[k] = (char *) 0; 
/*判断指令 arg[0]是否存在*/ 
if (is_fileexist(arg[0]) == -1 ) 
 { 
 printf("This command is not found !\n"); 
 for(i=0;i<k;i++) 
 free(arg[i]); 
 continue; 
 }
 
 int is_fileexist(char *comm) 
{ 
char * path, * p; 
int i; 
i = 0;

/*使用 getenv 函数来获取系统环境变量，用参数 PATH 表示获取路径*/ 
path = getenv("PATH"); 
p = path; 
while (*p != '\0') 
{ 
 /*路径列表使用':'来分隔路径*/ 
 if (*p != ':') 
 buffer[i++] = *p; 
 else 
 { 
 buffer[i++] = '/'; 
 buffer[i] = '\0'; 
/*将指令和路径合成，形成 pathname，并使用 access 函数来判断该文件是否存在*/ 
 strcat(buffer,comm); 
 if (access(buffer,F_OK) == 0) /*文件被找到*/ 
 return 0; 
 else 
 /*继续寻找其它路径*/ 
 i = 0; 
 } 
 p++; 
 } 
/*搜索完所有路径，依然没有找到则返回-1*/ 
return -1; 
}

/* fork a sub-process to run the execution file */ 
if ((pid = fork()) ==0) /*子进程*/ 
execv(buffer,arg); 
else /*父进程*/ 
if (is_back == 0) /*并非后台执行指令*/ 
waitpid(pid,&status,0);

/*释放申请的空间*/ 
for (i=0;i<k;i++) free(arg[i]); 
}


int pipel(char * input, int len) 
{ 
char * argv[2][30]; 
int I, j, k, count, is_back = 0; 
int li_comm = 0, fd[2], fpip[2]; 
char lc_char, lc_end[1]; 
pid_t child1, child2; 
/* 管道的命令解析过程*/ 
for(I = 0, j = 0, k = 0; I<=len; I++) 
{ 
 if(input[I]==' ' || input[I]=='\t' || input[I]=='\0' || input[I]=='|') 
 { 
 if(input[I] == '|') 
 { 
 /* 管道符号*/ 
 if(j > 0) 
 { 
 buffer[j++] = '\0'; 
/* 因为管道时连接的是两个指令，所以用二维数组指针来存放命令和参数， li_comm
是表示的几个指令*/ 
 argv[li_comm][k] = (char *) malloc(sizeof(char) * j); 
 strcpy(argv[li_comm][k++], buffer); 
 } 
 argv[li_comm][k++] = (char *)0; 
 /* 遇到管道符，第一个指令完毕，开始准备第二个指令*/ 
 li_comm ++; 
 count = k; 
 k = 0; j = 0; 
 } 
 if(j == 0) continue; 
 else 
 { 
 buffer[j++] = '\0';
 
 argv[li_comm][k] = (char *)malloc(sizeof(char) * j); 
 strcpy(argv[li_comm][k], buffer); 
 k ++; 
 } 
 j = 0; /* Initiate */ 
 } 
 else 
 { 
 if(input[I] == '&' && input[I + 1] == '\0') 
 { 
 is_back = 1; 
 continue; 
 } 
 buffer[j ++] = input[I]; 
 } 
} 
argv[li_comm][k ++] = (char *)0; 
if(is_fileexist(argv[0][0]) == -1) 
{ 
 printf("This first command is not found !\n"); 
 for(I = 0; I < count; I ++) free(argv[0][I]); 
 return 0; 
} 
/* 指令解析结束*/ 
 /* 建立管道*/ 
if(pipe(fd) == -1) 
{ 
 printf("Open pipe error !\n"); 
 return -1; 
} 
/*创建的一个子进程执行管道符前的命令，并将输出写道管道*/ 
if((child1 = fork()) == 0) 
{ 
 /*关闭读端*/ 
 close(fd[0]); 
 if(fd[1] != STDOUT_FILENO) 
 { 
 /* 将标准的输出重定向到管道的写入端，这样该子进程的输出就写入了管道*/ 
 if(dup2(fd[1], STDOUT_FILENO) == -1) 
 { 
 printf("Redirect Standard Out error !\n"); 
 return -1; 
 }
 
 /*关闭写入端*/ 
 close(fd[1]); 
 } 
 execv(buffer, argv[0]); 
 } 
 else 
 { 
 /*父进程*/ 
 /* 先要等待写入管道的进程结束*/ 
 waitpid(child1, &li_comm,0); 
 /* 然后必须写入一个结束标记，告诉读管道进程的数据到这里就完了*/ 
 lc_end[0] = 0x1a; 
 write(fd[1], lc_end, 1); 
 close(fd[1]); 
 if(is_fileexist(argv[1][0]) == -1) 
 { 
 printf("This command is not found!\n"); 
 for(I = 0; I < k; I ++) free(argv[1][I]); 
 return 0; 
 } 
 /* 创建第二个进程执行管道符后的命令，并从管道读输入流*/ 
 if((child2 = fork()) == 0) 
 { 
 if(fd[0] != STDIN_FILENO) 
 { 
 /* 将标准的输入重定向到管道的读入端*/ 
 if(dup2(fd[0], STDIN_FILENO) == -1) 
 { 
 printf("Redirect Standard In Error !\n"); 
 return -1; 
 } 
 close(fd[0]); 
 } 
 execv(buffer, argv[1]); 
 } 
 else /*父进程*/ 
 if(is_back == 0) waitpid(child2, NULL, 0); 
 } 
 for(I = 0; I < count; I ++) free(argv[0][I]); 
 for(I = 0; I < k; I ++) free(argv[1][I]); 
 return 0; 
}

int redirect(char *in,int len) 
{ 
char *argv[30],*filename[2]; 
pid_t pid; 
int i,j,k,fd_in,fd_out,is_in = -1,is_out = -1,num = 0; 
int is_back = 0,status=0; 
/*这里是重定向的命令解析过程，其中 filename 用于存放重定向文件，is_in, is_out 分别
是输入重定向标记和输出重定向标记*/ 
for (i = 0,j = 0,k = 0;i <= len;i++){ 
if (in[i]==' '||in[i]=='\t'||in[i]=='\0'||in[i] =='<'||in[i]=='>'){ 
if (in[i] == '>' || in[i] == '<'){ 
/*重定向指令最多'<'，'>'各出现一次，因此 num 最大为 2，否则认为命令输入错误*/ 
if (num < 3){ 
 num ++; 
 if (in[i] == '<') 
 is_in = num - 1; 
 else 
 is_out = num - 1; 
 
/*处理命令和重定向符号相连的情况，比如 ls>a*/ 
if (j > 0 && num == 1) { 
 buffer[j++] = '\0'; 
 argv[k] = (char *) malloc(sizeof(char)*j); 
 strcpy(argv[k],buffer); 
 k++; 
 j = 0;} 
 } 
else{ 
 printf("The format is error!\n"); 
 return -1; 
 } 
} 
if (j == 0) 
 continue; 
else{ 
 buffer[j++] = '\0'; 
 /*尚未遇到重定向符号，字符串是命令或参数*/ 
 if (num == 0){
	 
	 argv[k] = (char *) malloc(sizeof(char)*j); 
 strcpy(argv[k],buffer); 
 k++; 
 } 
 /*是重定向后符号的字符串，是文件名*/ 
 else{ 
 filename[status] = (char *) malloc(sizeof(char)*j); 
 strcpy(filename[status++],buffer); 
 } 
 j = 0; /*initate*/ 
 } 
 } 
else{ 
 if (in[i] == '&' && in[i+1] == '\0'){ 
 is_back = 1; 
 continue; 
 } 
 buffer[j++] = in[i]; 
 } 
 } 
argv[k] = (char *) 0; 
if (is_fileexist(argv[0]) == -1 ){ 
 printf("This command is not found!\n"); 
 for(i=0;i<k;i++) 
 free(argv[i]); 
 return 0; 
} 
if ((pid = fork()) ==0){ 
/*存在输出重定向*/ 
if (is_out != -1) 
 
if((fd_out=open(filename[is_out],O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR))==-1)
{ 
printf("Open out %s Error\n",filename[is_out]); 
return -1; 
} 
/*存在输入重定向*/ 
if (is_in != -1) 
if((fd_in=open(filename[is_in],O_RDONLY,S_IRUSR|S_IWUSR))==-1){ 
 printf("Open in %s Error\n",filename[is_out]); 
 return -1; 
}

if (is_out != -1) 
/*使用 dup2 函数将标准输出重定向到 fd_out 上，dup2(int oldfd,int newfd)实现的
*是把 oldfd 所指的文件描述符复制到 newfd。若 newfd 为一已打开的文件描述词，
*则 newfd 所指的文件会先被关闭，dup2 复制的文件描述词与原来的文件描述词
*共享各种文件状态*/ 
if(dup2(fd_out,STDOUT_FILENO)==-1){ 
 printf("Redirect Standard Out Error\n"); 
 exit(1); 
} 
if (is_in != -1) 
if(dup2(fd_in,STDIN_FILENO)==-1){ 
 printf("Redirect Standard Out Error\n"); 
 exit(1); 
} 
execv(buffer,argv); 
} 
 else 
 if (is_back == 0) /*run on the TOP*/ 
 waitpid(pid,&status,0); 
 for (i=0;i<k;i++) 
 free(argv[i]); 
 if (is_in != -1){ 
 free(filename[is_in]); 
 close(fd_in); 
 } 
 if (is_out != -1){ 
 free(filename[is_out]); 
 close(fd_out); 
 } 
 return 0; 
}